package org.zhuzx.util;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 列表数据处理工具类
 * @author	zhuzx	2016年10月10日
 */
@SuppressWarnings("unchecked")
public class ListDataUtil {

	/**
	 * 获取指定键的值组成的列表
	 */
	public static <K,V> List<V> getValues(List<Map<K,V>> list, K key) {
		List<V> values = new ArrayList<V>(list.size());
		for (Map<K,V> map : list) {
			values.add(map.get(key));
		}
		return values;
	}
	
	/**
	 * 以指定的键对多个ListMap做外连接
	 * @param key 连接的主键
	 * @param listArray 注意，原list中的元素会被修改（虽然列表容器本身不变）。
	 * @return 一个合并后的新ListMap
	 */
	@SafeVarargs
	public static <K,V> List<Map<K,V>> fullOutJoin(final K key,
			List<Map<K,V>>... listArray) {
		Comparator<Map<K,V>> comparator = new Comparator<Map<K,V>>() {
			@SuppressWarnings("deprecation")
			public int compare(Map<K,V> o1, Map<K,V> o2) {
				Comparable<V> c = (Comparable<V>) o1.get(key);
				int result = c.compareTo(o2.get(key));
				if (result == 0) {
					result = GeneralUtil.compareObjectWithoutEqual(o1, o2);
				}
				return result;
			}
		};
		return fullOutJoin(key, comparator, listArray);
	}
	
	/**
	 * 以指定的键对多个ListMap做外连接
	 * @param key 连接的主键
	 * @param comparator 必须保证所有元素不等，并且以key作为第一排序依据。
	 * @param listArray 注意，原list中的元素会被修改（虽然列表容器本身不变）。
	 * @return 一个合并后的新ListMap
	 */
	@Deprecated//用List进行sort与TreeSet的优劣
	public static <K,V> List<Map<K,V>> fullOutJoin(K key, Comparator<Map<K,V>> comparator,
			List<Map<K,V>>... listArray) {
		boolean existData = false;
		for (List<Map<K,V>> list : listArray) {
			if (!GeneralUtil.isEmpty(list)) {
				existData = true;
			}
		}
		if (!existData) {
			return new ArrayList<>();
		}
		TreeSet<Map<K,V>> fullSet = new TreeSet<Map<K,V>>(comparator);
		for (List<Map<K,V>> list : listArray) {
			fullSet.addAll(list);
		}
		Iterator<Map<K,V>> ite = fullSet.iterator();
		Map<K,V> mergeInto = ite.next();
		while (ite.hasNext()) {
			Map<K,V> element = ite.next();
			if (element.get(key).equals(mergeInto.get(key))) {
				mergeInto.putAll(element);
				ite.remove();
			} else {
				mergeInto = element;//末尾元素不需要处理
			}
		}
		return new ArrayList<Map<K,V>>(fullSet);
	}
	
	/**
	 * 以指定的键对两个ListMap做左连接
	 * @param masterList 主列表，其中的元素会被修改（虽然列表容器本身不变）。
	 * @param key 连接的键
	 * @param detailList 从列表，列表元素和列表容器都不会被修改。
	 * @param allowMultiDetail 是否允许从列表中的元素在key值上有重复
	 */
	//@Deprecated//检查compareTo与equals方法的统一性，妥善处理一对多的情况（相当于必须选取分组内的某条，当前是取第一条）。
	public static <K,V> void leftOutJoin(List<Map<K,V>> masterList,
			K key, List<Map<K,V>> detailList, boolean allowMultiDetail) {
		if (!allowMultiDetail) {
			//TODO
			throw new RuntimeException("暂不支持");
		}
		List<Map<K,V>> master = new ArrayList<Map<K,V>>(masterList);
		sortListMap(master, key);
		List<Map<K,V>> detail = new ArrayList<Map<K,V>>(detailList);
		sortListMap(detail, key);
		ListIterator<Map<K,V>> detailIte = detail.listIterator();
		for (Map<K,V> row : master) {
			Comparable<V> c = (Comparable<V>) row.get(key);
			while (detailIte.hasNext()) {
				Map<K,V> detRow = detailIte.next();
				if (detRow.get(key) == null) {
					continue;
				}
				int result = c.compareTo(detRow.get(key));
				if (result <= 0) {
					if (result == 0) {
						row.putAll(detRow);
					}
					//回退一下从列表的迭代器，以便后一个主元素跟这个从元素进行匹配。
					//即使当前已匹配上也要回退，一种特殊情况是，后一个主元素与当前主元素相等，自然也需要匹配这个从元素。
					detailIte.previous();
					break;
				} else {//继续滚动从迭代器，取下一个从元素与当前主元素进行匹配。
					continue;
				}
			}
		}
	}
	
	/**
	 * 以指定的键对两个ListMap做内连接
	 * @param listA 列表A
	 * @param key 连接的键，两个list参数互换不影响结果。
	 * @param listB 列表B
	 * @return 一个合并后的新ListMap，原列表中的元素会被修改（虽然列表容器本身不变）。
	 */
	//@Deprecated//性能较差的实现，执行逻辑有冗余，需解除对leftOutJoin的依赖。
	public static <K,V> List<Map<K,V>> innerJoin(List<Map<K,V>> listA, K key, List<Map<K,V>> listB) {
		if (GeneralUtil.isEmpty(listA) || GeneralUtil.isEmpty(listB)) {
			return new LinkedList<>();
		}
		Set<K> keySetA = new HashSet<>(listA.get(0).keySet());
		Set<K> keySetB = new HashSet<>(listB.get(0).keySet());
		keySetB.removeAll(keySetA);//剩余的都是listB中独有的键
		if (keySetB.size() == 0) {
			throw new RuntimeException("无效的内连接操作！");
		}
		K identKey = null;
		for (K aKey : keySetB) {
			identKey = aKey;
			break;
		}
		leftOutJoin(listA, key, listB, false);
		List<Map<K,V>> result = new LinkedList<>(listA);
		Iterator<Map<K,V>> ite = result.iterator();
		while (ite.hasNext()) {
			if (!ite.next().containsKey(identKey)) {//listA中未匹配的元素不会含listB的独有键
				ite.remove();
			}
		}
		return result;
	}
	
	/**
	 * 以指定的键对ListMap进行排序
	 */
	public static <K,V> void sortListMap(List<Map<K,V>> list, final K key) {
		Comparator<Map<K,V>> comparator = new Comparator<Map<K,V>>() {
			public int compare(Map<K, V> o1, Map<K, V> o2) {
				Comparable<V> c = (Comparable<V>) o1.get(key);
				return c.compareTo(o2.get(key));
			}
		};
		Collections.sort(list, comparator);
	}
	
	/**
	 * 获取满足compareTo==0条件的记录组成的子列表
	 */
	public static <K,V> List<Map<K,V>> getSubList(List<Map<K,V>> list,
			Comparable<? super Map<K,V>> comparable) {
		List<Map<K,V>> subList = new ArrayList<Map<K,V>>();
		for (Map<K,V> map : list) {
			if (comparable.compareTo(map) == 0) {
				subList.add(map);
			}
		}
		return subList;
	}
	
	/**
	 * 获取头尾两个元素的指定键值
	 * @return 返回一个长度为2的数组
	 */
	public static <K,V> String[] getHeadTailValue(List<Map<K,V>> list, K field) {
		String[] result = new String[2];
		if (list.size() == 0) {
			return result;
		}
		result[0] = String.valueOf(list.get(0).get(field));
		result[1] = String.valueOf(list.get(list.size() - 1).get(field));
		return result;
	}
	
	/**
	 * 检查指定键值是否为空，为空则抛出异常。
	 */
	private static <K,V> void checkNotNull(List<Map<K,V>> list, K key) {
		for (Map<K,V> map : list) {
			if (map.get(key) == null) {
				throw new RuntimeException(key + "键值不可为空！\n" + map);
			}
		}
	}
	
	/**
	 * 根据指定键对齐两个List<br>
	 * 注：若uniqKey的值实际上不唯一，也无负面影响。
	 */
	@SuppressWarnings("unused")
	private static <K,V> void alignList(List<Map<K,V>> list1, List<Map<K,V>> list2, K uniqKey) {
		checkNotNull(list1, uniqKey);
		checkNotNull(list2, uniqKey);
		int maxLength = Math.max(list1.size(), list2.size());
		List<Map<K,V>> tempList = new ArrayList<>(maxLength);
		for (int i=0; i<maxLength; i++) {
			tempList.add(null);
		}
		int oldLength = list1.size();
		for (int i=list2.size()-1; i>=0; i--) {
			Map<K,V> row = list2.get(i);
			for (int j=list1.size()-1; j>=0; j--) {
				Map<K,V> target = list1.get(j);
				if (row.get(uniqKey).equals(target.get(uniqKey))) {
					tempList.set(i, target);
					list1.remove(j);
					break;
				}
			}
		}
		for (int i=list1.size()-1; i>=0; i--) {
			//无法根据key找到对应正确记录的，填充到其它位置。
			//若list1多于list2，则tempList长度为list1，刚好容纳全部，否则转移全部list1后还有空位。
			for (int j=tempList.size()-1; j>=0; j--) {
				if (tempList.get(j) == null) {
					tempList.set(j, list1.remove(i));
					break;//已插入空位，重新从list1取元素。
				}
			}
		}
		if (list2.size() < oldLength) {//补齐list2
			for (int i=list2.size(); i<oldLength; i++) {
				list2.add(new HashMap<K,V>());
			}
		} else {//tempList还留有空位，占满补齐。
			for (int i=0; i<tempList.size(); i++) {
				if (tempList.get(i) == null) {
					tempList.set(i, new HashMap<K,V>());
				}
			}
		}
		list1.clear();
		list1.addAll(tempList);
	}
	
	/**
	 * 按指定键的值分组分割为多个List<br>
	 * 注意：返回的各个子List是基于原List的视图，所以双方都不可修改结构，并且修改元素时会同时影响双方。
	 * 注意：若原列表未排序（isAlreadySorted参数为false），则会在本方法内排序，原列表的元素顺序会被改变。
	 * @param list 原始列表
	 * @param key 需要比对的键名，该键名对应的值必须实现Comparable接口。
	 * @param isAlreadySorted 列表是否已经排序
	 * @return 由多个子列表视图组成的复合列表，指定键的值相同的元素都在同一个子列表中。
	 */
	public static <K,V> List<List<Map<K,V>>> divideListByGroup(
			List<Map<K,V>> list, K key, boolean isAlreadySorted) {
		if (list == null) {
			throw new IllegalArgumentException();
		}
		if (list.size() == 0) {
			return new ArrayList<>();
		}
		if (!isAlreadySorted) {
			sortListMap(list, key);
		}
		List<List<Map<K,V>>> result = new ArrayList<>();
		int fromIndex = 0;
		int toIndex = fromIndex + 1;
		int outIndex = list.size();
		Comparable<V> groupHeaderComp = (Comparable<V>) list.get(fromIndex).get(key);
		while (toIndex < outIndex) {
			V value = list.get(toIndex).get(key);
			if (groupHeaderComp.compareTo(value) != 0) {
				result.add(list.subList(fromIndex, toIndex));
				fromIndex = toIndex;
				groupHeaderComp = (Comparable<V>) value;
			}
			toIndex++;
		}
		if (fromIndex < outIndex) {//toIndex越界后循环直接结束，最后一组数据还未加入。
			result.add(list.subList(fromIndex, outIndex));
		}
		return result;
	}
	
	/**
	 * 修改列表中所有Map的指定键名，值不变。
	 */
	public static <K,V> void modifyKeyName(List<Map<K,V>> list, K oldKey, K newKey) {
		if (oldKey.equals(newKey)) {
			return;
		}
		for (Map<K,V> map : list) {
			map.put(newKey, map.remove(oldKey));
		}
	}
	
	/**
	 * @param list 当传入混合list时，confirmedIsUpperCase不能为false，否则可能会做空处理。
	 * @param confirmedIsUpperCase 注意：若传入false，则会检查第一个Map的全部key是否存在大写，若没有则认为不需要转换。
	 * 								所以传入false时务必保证list中的所有Map都是一致的key，也就是不要同时传入一个混合list。
	 */
	public static <V> void modifyKeyNameToLowerCase(List<Map<String,V>> list, boolean confirmedIsUpperCase) {
		if (GeneralUtil.isEmpty(list)) {
			return;
		}
		if (!confirmedIsUpperCase) {
			Map<String,V> first = list.get(0);
			boolean existUpper = false;
			outer:
			for (String key : first.keySet()) {
				for (int i = 0; i < key.length(); i++) {
					existUpper = Character.isUpperCase(key.charAt(i));
					if (existUpper) {
						break outer;
					}
				}
			}
			if (!existUpper) {
				return;
			}
		}
		List<String> keysTemp = new ArrayList<>();
		for (Map<String,V> map : list) {
			keysTemp.addAll(map.keySet());
			for (String key : keysTemp) {
				map.put(key.toLowerCase(), map.remove(key));
			}
			keysTemp.clear();
		}
	}
	
	/**
	 * 移除Map中的指定键值对
	 */
	public static <K,V> void removeKey(List<Map<K,V>> list, K... keys) {
		for (Map<K,V> map : list) {
			for (K key : keys) {
				map.remove(key);
			}
		}
	}
	
	/**
	 * 移除列表中的如下元素：指定键值中任意一个为空。
	 */
	public static <K,V> void removeElementOfBlankValue(
			List<Map<K,V>> list, K... keys) {
		List<Map<K,V>> remaining = new ArrayList<>(list.size());
		outer:
		for (Map<K,V> map : list) {
			for (K key : keys) {
				if (GeneralUtil.isBlank(map.get(key))) {
					continue outer;//未留在remaining中即代表被移除
				}
			}
			remaining.add(map);
		}
		if (remaining.size() != list.size()) {
			list.clear();
			list.addAll(remaining);
		}
	}
	
	/**
	 * 移除列表中的如下元素：指定键的值等于给出的value值。<br>
	 * 注意：无法处理空值的情况，比如参数value为空（会报错），或者列表元素的指定键值为空（会仍旧保留）。
	 */
	public static <K,V> void removeElementOfSpecifiedValue(
			List<Map<K,V>> list, K key, V value) {
		List<Map<K,V>> remaining = new ArrayList<>(list.size());
		for (Map<K,V> map : list) {
			if (!value.equals(map.get(key))) {//等于value的不保留
				remaining.add(map);
			}
		}
		if (remaining.size() != list.size()) {
			list.clear();
			list.addAll(remaining);
		}
	}
	
	/**
	 * 聚合类型定义，每种类型实现两个简单操作。<br>
	 * 注意：不支持avg。
	 */
	private static enum AggregateType {
		COUNT {
			BigDecimal getInitialValue(BigDecimal newMember) {
				return (newMember == null) ? BigDecimal.ZERO : BigDecimal.ONE;
			}
		},
		SUM,
		MAX {
			BigDecimal addGroupMember(BigDecimal groupValue, BigDecimal newMember) {
				if (newMember == null) {
					return groupValue;
				} else {
					return (newMember.compareTo(groupValue) > 0) ? newMember : groupValue;
				}
			}
		},
		MIN {
			BigDecimal addGroupMember(BigDecimal groupValue, BigDecimal newMember) {
				if (newMember == null) {
					return groupValue;
				} else {
					return (newMember.compareTo(groupValue) < 0) ? newMember : groupValue;
				}
			}
		};
		/**
		 * 组内当前没有其它记录时，将这个值转化为聚合结果并返回。
		 */
		BigDecimal getInitialValue(BigDecimal newMember) {
			return (newMember == null) ? BigDecimal.ZERO : newMember;
		}
		/**
		 * 组内已有一个当前的聚合结果时，将这个值加入组，重新计算聚合结果并返回。
		 */
		BigDecimal addGroupMember(BigDecimal groupValue, BigDecimal newMember) {
			return groupValue.add(getInitialValue(newMember));
		}
	}
	
	/**
	 * 对指定字段执行聚合操作，详见内部调用的重载方法。
	 */
	public static <K,V> List<Map<K,Object>> aggregateField(List<Map<K,V>> list,
			String aggregateType, K targetField,
			boolean isAlreadySorted, boolean onlyOutputGroupByFields, K... groupBy) {
		return aggregateField(list, new String[] {aggregateType}, (K[]) Arrays.asList(targetField).toArray(),
				isAlreadySorted, onlyOutputGroupByFields, groupBy);
	}
	
	/**
	 * 对指定字段执行聚合操作
	 * @param list 原始数据，本方法不会修改列表元素或者列表容器。
	 * @param aggregateTypeArray 聚合类型，传入count、sum、max等标识，不区分大小写。跟后面参数中的聚合字段名一一对应。
	 * @param targetFieldArray 要聚合的字段名，其值可以是任何数值类型，或者String格式的数字值（支持科学计数法格式）。
	 * @param isAlreadySorted 若调用方无法保证这个list中的元素是已经按groupBy排好序的，传入false，方法内部会重新排序。
	 * @param onlyOutputGroupByFields 若想要返回的记录中只包含groupBy参数指定的字段和聚合值targetField，则传true。
	 * @param groupBy 分组字段名，需要保证原始数据的每个分组字段值都不为null。若未传入任何分组字段，则将整个list作为一个组执行聚合。
	 * @return 新的列表，其中每条记录的targetField键存放聚合后的新值，注意：该值固定是BigDecimal类型，不再是目标字段值的原类型。
	 */
	public static <K,V> List<Map<K,Object>> aggregateField(List<Map<K,V>> list,
			String[] aggregateTypeArray, K[] targetFieldArray,
			boolean isAlreadySorted, boolean onlyOutputGroupByFields, K... groupBy) {
		if (GeneralUtil.isEmpty(list)) {
			return (list == null) ? null : new ArrayList<>(0);
		} else {
			list = new ArrayList<>(list);
		}
		if (aggregateTypeArray.length != targetFieldArray.length)
			throw new IllegalArgumentException("传入的聚合操作符与聚合字段列表数量必须一致，否则无法一一对应。");
		Comparator<Map<K,V>> comp = GeneralUtil.createMapComparator(null, groupBy);
		if (!isAlreadySorted)
			Collections.sort(list, comp);
		AggregateType[] aggrArray = new AggregateType[aggregateTypeArray.length];
		for (int i = 0; i < aggrArray.length; i++)
			aggrArray[i] = AggregateType.valueOf(aggregateTypeArray[i].toUpperCase());
		BigDecimal[] groupValueArray = new BigDecimal[targetFieldArray.length];
		Arrays.fill(groupValueArray, BigDecimal.ZERO);
		List<Map<K,Object>> resultList = new ArrayList<>();
		Map<K,V> groupFirst = list.get(0);
		boolean atSameGroup = false;
		boolean isLinkedMap = list.get(0) instanceof LinkedHashMap;
		list.add(new HashMap<K,V>(0));//加一个伪元素以便多循环一次，用于处理末尾行是单独一个分组的情况。
		for (Map<K,V> row : list) {
			atSameGroup = (row.size() == 0) ? false : comp.compare(groupFirst,row) == 0;
			if (atSameGroup) {
				for (int i = 0; i < groupValueArray.length; i++)
					groupValueArray[i] = aggrArray[i].addGroupMember(groupValueArray[i],
							GeneralUtil.getBigDecimalValue(row.get(targetFieldArray[i])));
			} else {//游标已经移动到了下一个分组，将前面已经完成聚合的那个分组的数据加入结果集。
				Map<K,Object> aGroup;
				if (onlyOutputGroupByFields) {
					aGroup = isLinkedMap ? new LinkedHashMap<K,Object>(groupBy.length + targetFieldArray.length)
							: new HashMap<K,Object>(groupBy.length + targetFieldArray.length);
					for (K key : groupBy)
						aGroup.put(key, groupFirst.get(key));
				} else {
					aGroup = isLinkedMap ? new LinkedHashMap<>((Map<K,Object>) groupFirst)
							: new HashMap<>((Map<K,Object>) groupFirst);
				}
				for (int i = 0; i < groupValueArray.length; i++)
					aGroup.put(targetFieldArray[i], groupValueArray[i]);
				resultList.add(aGroup);
				groupFirst = row;
				for (int i = 0; i < groupValueArray.length; i++)
					groupValueArray[i] = aggrArray[i].getInitialValue(
							GeneralUtil.getBigDecimalValue(row.get(targetFieldArray[i])));
			}
		}
		return resultList;
	}

	/**
	 * 检查列表中的元素是否跟期望的数据列表一一对应，不管有多的还是少的都会抛出BizException。
	 * @param data 需要检查的数据列表
	 * @param standard 期望的数据列表，data中的元素必须和此列表中的元素一一对应。
	 * @param dataItemDesc 元素个体描述（可读的业务性文本）
	 * @param useStrictMode 是否使用严格模式，若是，则将严格按顺序比对，
	 *                         否则只要求两边的元素在对方列表中唯一存在（相当于作为普通的Set处理）。   
	 */
	public static <E> void checkOneOneCorrespondence(List<E> data, List<E> standard,
													 String dataItemDesc, boolean useStrictMode) {
		if (standard.stream().distinct().count() != standard.size()) {
			throw new BizException("检测到目标列表参数本身含有重复项：" + standard);
		}
		if (data.size() == standard.size()) {
			boolean isEquals = useStrictMode ? data.equals(standard) :
					data.containsAll(standard) && standard.containsAll(data);
			BizAssert.mustTrue(isEquals, "参数匹配失败：" + dataItemDesc + "无法和预期数据一一对应");
		}
		if (data.size() < standard.size()) {
			List<E> copy = new ArrayList<>(standard);
			copy.removeAll(data);
			throw new BizException("检测到缺失" + dataItemDesc + "：" + copy);
		} else if (data.size() > standard.size()) {//数量多出来了，说明有重复，将这些重复项找出来。
			Map<E, Long> countMap = data.stream().collect(
					Collectors.groupingBy(item -> item, Collectors.counting()));
			List<E> conflicting = countMap.entrySet().stream().filter(item -> item.getValue() > 1)
					.map(Map.Entry::getKey).collect(Collectors.toList());
			throw new BizException("检测到重复的" + dataItemDesc + "：" + conflicting);
		}
	}

	/**
	 * 将目标数据列表与参照数据列表进行比对，提取出有差异的全部个体，
	 * 并用三元值标识这些数据个体相对于参照数据发生了何种类型的变化。<br>
	 * 本方法不会严格按位置比对，也就是不在意数据在列表中的相对位置，而会从对方列表中自动匹配相同标识字段值的数据。
	 * 若标识字段值相同则认为该数据在两个列表中都存在，再进一步判断是否有具体的内容差异。
	 * @param data 需要处理的目标数据列表，比如用户本次操作生成的一批新数据。
	 * @param primaryKey 获取标识字段值，作为一条数据是否已经存在的判断依据。
	 *                   注：此参数不一定要用数据库的主键id，能够在业务上唯一标识该条数据的字段皆可（只需保证在列表范围内）。   
	 * @param standard 参照数据列表，作为参照物供比对，比如库中的数据。
	 * @param fieldsNeedCompare 用来决定如何判断某条数据是否与参照数据之间存在差异，未传入则用对象自己的equals方法。
	 * @return 返回所有有差异的数据，这些数据对象分为三个List存放，并关联在整型三元值下面：
	 * 1是比standard多出来的；-1是比standard缺少的；0是能匹配但内容不同的。<br>
	 * 也就是说返回的数据对象按三元值分成了连续的三组，并且每组内的元素顺序符合这些数据在源列表中的原顺序。<br>
	 * 注一：一定会返回三个键值对，若某个key下面实际没有元素也会关联一个空的List作为value。<br>
	 * 注二：返回的三个子List都是新构建的列表，但其中的对象元素仍旧是参数列表中的对象，
	 * 其中1和0的元素来自data，-1的元素来自standard（因为data中没有）。
	 */
	@SafeVarargs
	public static <E> Map<Integer,List<E>> extractDifferentItem(List<E> data,
											   Function<E,Object> primaryKey, List<E> standard,
											   Function<E,Object>... fieldsNeedCompare) {
		Map<Object, E> dataMap = data.stream().collect(Collectors.toMap(primaryKey, item -> item));
		Assert.notAllow(dataMap.size() != data.size(), "目标数据列表的标识字段值不可重复");
		Map<Object, E> standardMap = standard.stream().collect(Collectors.toMap(primaryKey, item -> item));
		Assert.notAllow(standardMap.size() != standard.size(), "参照数据列表的标识字段值不可重复");
		Set<Object> dataKeys = data.stream().map(primaryKey).collect(Collectors.toCollection(LinkedHashSet::new));
		Set<Object> standardKeys = standard.stream().map(primaryKey).collect(Collectors.toCollection(LinkedHashSet::new));
		Set<Object> keysAdd = new LinkedHashSet<>(dataKeys);
		keysAdd.removeAll(standardKeys);
		List<E> itemsAdd = keysAdd.stream().map(dataMap::get).collect(Collectors.toList());
		Map<Integer,List<E>> diffResult = new LinkedHashMap<>();
		diffResult.put(1, itemsAdd);
		Set<Object> keysRemove = new LinkedHashSet<>(standardKeys);
		keysRemove.removeAll(dataKeys);
		List<E> itemsRemove = keysRemove.stream().map(standardMap::get).collect(Collectors.toList());
		diffResult.put(-1, itemsRemove);
		dataKeys.retainAll(standardKeys);
		List<E> itemsModify = dataKeys.stream().filter(key -> {
			//若传入了具体的比较字段，则逐个比较这些字段值，否则用元素自己的equals方法比对。
			if (!GeneralUtil.isEmpty(fieldsNeedCompare)) {
				return Arrays.stream(fieldsNeedCompare).anyMatch(
						//实测给Objects.equals方法传入两个null也会得到true
						field -> !Objects.equals(
								field.apply(dataMap.get(key)), field.apply(standardMap.get(key))));
			} else {
				return dataMap.get(key).equals(standardMap.get(key));
			}
		}).map(dataMap::get).collect(Collectors.toList());
		diffResult.put(0, itemsModify);
		return diffResult;
	}

	/**
	 * 将目标数据列表与参照数据列表进行比对，提取出有差异的全部个体，严格模式，只按位置进行比对。
	 * @see #extractDifferentItem(List, Function, List, Function[])
	 */
	@SuppressWarnings("unused")
	@SafeVarargs
	private static <E> Map<Integer,List<E>> extractDifferentItem(List<E> data, List<E> standard,
												Function<E,Object>... fieldsNeedCompare) {
		Assert.notAllow(GeneralUtil.isEmpty(fieldsNeedCompare), "未传入具体的字段名，无法进行比较。");
		int intersectionLength = Math.min(data.size(), standard.size());
		Map<Integer,List<E>> diffResult = new LinkedHashMap<>();
		String collisionMsg = "发现冲突，对象有重复或者自定义的equals方法实现有误。";
		if (data.size() > intersectionLength) {
			diffResult.put(1, new ArrayList<>(data.subList(intersectionLength, data.size())));
		}
		if (standard.size() > intersectionLength) {
			diffResult.put(-1, new ArrayList<>(standard.subList(intersectionLength, standard.size())));
		}
		List<E> itemsModify = new ArrayList<>();
		for (int i = 0; i < intersectionLength; i++) {
			E dataItem = data.get(i);
			E standardItem = standard.get(i);
			if (Arrays.stream(fieldsNeedCompare).anyMatch(
					field -> !Objects.equals(field.apply(dataItem), field.apply(standardItem)))) {
				itemsModify.add(dataItem);
			}
		}
		diffResult.put(0, itemsModify);
		return diffResult;
	}

	/**
	 * 从列表中剔除重复的元素
	 */
	public static <E> void excludeDuplicateElement(List<E> list) {
		Set<E> set = new LinkedHashSet<>(list);
		list.clear();
		list.addAll(set);
	}

	/**
	 * 将一个List分割为多个小批的List<br>
	 * 注意：返回的各个子List是基于原List的视图，所以双方都不可修改结构，并且修改元素时会同时影响双方。
	 */
	public static <E> List<List<E>> divideList(List<E> list, int batchSize) {
		if (list.size() == 0) {
			return new ArrayList<>(0);
		}
		int nums = list.size() / batchSize;
		if (list.size() % batchSize > 0) {
			nums++;
		}
		List<List<E>> result = new ArrayList<>(nums);
		int fromIndex = 0;
		int toIndex = batchSize;
		while (toIndex <= list.size()) {
			result.add(list.subList(fromIndex, toIndex));
			fromIndex = toIndex;
			toIndex += batchSize;
		}
		if (fromIndex < list.size()) {
			result.add(list.subList(fromIndex, list.size()));
		}
		return result;
	}

	/**
	 * 将data数据列表中的元素按照particularSortedKeys指定的key值的顺序排序。<br>
	 * 两个列表都允许有key值重复，对于data中key值重复的元素，会按它们在原列表中的相对顺序紧凑排列。<br>
	 * 注意，两个列表都不能有空元素。
	 * @param data 原始数据列表，执行本方法将改变列表中元素顺序（但不会修改元素本身）。
	 * @param getKey 从原始数据中取得key值作为排序依据
	 * @param particularSortedKeys 若data中元素得到的key值未在这个列表中列出，则该元素会被移至data的末尾。<br>
	 *                             若存在多个这样的未匹配元素，则仍旧会按它们在原列表中的相对顺序排列（直到末尾）。
	 */
	public static <E,K> void particularSortList(List<E> data, Function<E,K> getKey, List<K> particularSortedKeys) {
		Map<K,List<E>> mapping = new LinkedHashMap<>(particularSortedKeys.size());
		particularSortedKeys.forEach(key -> mapping.put(key, null));
		List<E> unmatched = new ArrayList<>();
		data.forEach(e -> {
			K key = getKey.apply(e);
			if (mapping.containsKey(key)) {
				List<E> sameKeySubList = mapping.computeIfAbsent(key, k -> new ArrayList<>(1));
				sameKeySubList.add(e);
			} else {
				unmatched.add(e);
			}
		});
		data.clear();
		mapping.values().forEach(subList -> {
			if (subList != null) {
				data.addAll(subList);
			}
		});
		if (!unmatched.isEmpty()) {
			data.addAll(unmatched);
		}
	}

}